/*****************************************************************************
 *  ENTROPY - emerging network to reduce orwellian potency yield
 *
 *  Copyright (C) 2002 Juergen Buchmueller <pullmoll@stop1984.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundation,
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 *	$Id: uri.c,v 1.2 2005/07/12 23:12:29 pullmoll Exp $
 *****************************************************************************/
#include "osd.h"
#include "memalloc.h"
#include "uri.h"
#include "base64.h"
#include "logger.h"

int is_valid_uri(chkey_t *key, const char *uri)
{
	FUN("is_valid_uri");

	if (0 == strncmp(uri, URI_PROTO, strlen(URI_PROTO))) {
		uri += strlen(URI_PROTO);
	} else if (0 == strncmp(uri, URI_PROTO_ALT, strlen(URI_PROTO_ALT))) {
		uri += strlen(URI_PROTO_ALT);
	}
	if ('C' == toupper(uri[0]) &&
		'H' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		return is_valid_chk(key, uri + 4);
	}
	if ('S' == toupper(uri[0]) &&
		'S' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		return is_valid_ssk(key, uri + 4);
	}
	if ('K' == toupper(uri[0]) &&
		'S' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		return is_valid_ksk(key, uri + 4);
	}
	return is_valid_ksk(key, uri);
}

int is_valid_chk(chkey_t *key, const char *uri)
{
	int rc = 0;
	FUN("is_valid_chk");

	if (0 == strncmp(uri, URI_PROTO, strlen(URI_PROTO))) {
		uri += strlen(URI_PROTO);
	} else if (0 == strncmp(uri, URI_PROTO_ALT, strlen(URI_PROTO_ALT))) {
		uri += strlen(URI_PROTO_ALT);
	}
	if ('C' == toupper(uri[0]) &&
		'H' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		uri += 4;
	}
	if (0 != (rc = str_key(key, uri))) {
		LOG(L_ERROR,("invalid CHK@ format '%s' (%d)\n", uri, rc));
	}

	return rc;	
}

int is_valid_ssk(chkey_t *key, const char *uri)
{
	int rc = 0;
	FUN("is_valid_ssk");

	if (0 == strncmp(uri, URI_PROTO, strlen(URI_PROTO))) {
		uri += strlen(URI_PROTO);
	} else if (0 == strncmp(uri, URI_PROTO_ALT, strlen(URI_PROTO_ALT))) {
		uri += strlen(URI_PROTO_ALT);
	}
	if ('S' == toupper(uri[0]) &&
		'S' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		uri += 4;
	}
	if (0 != (rc = str_ssk(key, uri))) {
		LOG(L_ERROR,("invalid SSK@ format '%s' (%d)\n", uri, rc));
		goto bailout;
	}
	if (key->type[0] == MSB(K_SSK_P) && key->type[1] == LSB(K_SSK_P)) {
		LOG(L_MINOR,("public SSK@ '%s'\n", uri));
	} else {
		chkey_t priv = *key;
		LOG(L_MINOR,("private SSK@ '%s'\n", uri));
		priv.log2size = log2size(SHA1SIZE);
		priv.type[0] = MSB(K_SSK_S);
		priv.type[1] = LSB(K_SSK_S);
		if (0 != (rc = key_ssk_pub_from_priv(key, &priv))) {
			LOG(L_MINOR,("key_ssk_pub_from_priv() call failed (%d)\n", rc));
			goto bailout;
		}
		LOG(L_MINOR,("public %s\n", key_long(key)));
	}

bailout:
	return rc;	
}

int is_valid_ksk(chkey_t *key, const char *uri)
{
	static const char uri_invalid[] = "&*?<>|";
	size_t len = 0;
	int rc = 0;
	FUN("is_valid_ksk");

	if (0 == strncmp(uri, URI_PROTO, strlen(URI_PROTO))) {
		uri += strlen(URI_PROTO);
	} else if (0 == strncmp(uri, URI_PROTO_ALT, strlen(URI_PROTO_ALT))) {
		uri += strlen(URI_PROTO_ALT);
	}
	if ('K' == toupper(uri[0]) &&
		'S' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		uri += 4;
	}
	while (uri[len]) {
		if (strchr(uri_invalid, uri[len])) {
			if (len == 0) {
				LOG(L_ERROR,("invalid KSK character '%c'\n", uri[len]));
				errno = EINVAL;
				rc = -1;
				break;
			}
			break;
		}
		len++;
	}

	if (0 == rc && NULL != key) {
		rc = create_chk_from_ksk(key, uri);
	}

	return rc;
}

int is_valid_bit(chkey_t *key, const char *uri)
{
	int rc = 0;
	FUN("is_valid_prv");

	if (0 == strncmp(uri, URI_PROTO, strlen(URI_PROTO))) {
		uri += strlen(URI_PROTO);
	} else if (0 == strncmp(uri, URI_PROTO_ALT, strlen(URI_PROTO_ALT))) {
		uri += strlen(URI_PROTO_ALT);
	}
	if ('B' == toupper(uri[0]) &&
		'I' == toupper(uri[1]) &&
		'T' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		uri += 4;
	}
	if (0 != (rc = str_key(key, uri))) {
		LOG(L_ERROR,("invalid bit-key format '%s' (%d)\n", uri, rc));
	}

	return rc;	
}

int is_chk(const char *uri)
{
	int rc = 0;
	if (0 == strncmp(uri, URI_PROTO, strlen(URI_PROTO))) {
		uri += strlen(URI_PROTO);
	} else if (0 == strncmp(uri, URI_PROTO_ALT, strlen(URI_PROTO_ALT))) {
		uri += strlen(URI_PROTO_ALT);
	}
	if ('C' == toupper(uri[0]) &&
		'H' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		uri += 4;
		rc = 0;
	} else {
		errno = EINVAL;
		rc = -1;
	}
	return rc;
}

int is_ssk(const char *uri)
{
	int rc = 0;
	if (0 == strncmp(uri, URI_PROTO, strlen(URI_PROTO))) {
		uri += strlen(URI_PROTO);
	} else if (0 == strncmp(uri, URI_PROTO_ALT, strlen(URI_PROTO_ALT))) {
		uri += strlen(URI_PROTO_ALT);
	}
	if ('S' == toupper(uri[0]) &&
		'S' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		uri += 4;
		rc = 0;
	} else {
		errno = EINVAL;
		rc = -1;
	}
	return rc;
}

char *normalize_uri(const char *uri)
{
	size_t len, size = strlen(uri) + 1;
	char *normalized;
	int type = 0;
	FUN("normalize_uri");

	/* normalize the given entropy:XYZ@ key */
	if (0 == strncmp(uri, URI_PROTO, strlen(URI_PROTO))) {
		uri += strlen(URI_PROTO);
	} else if (0 == strncmp(uri, URI_PROTO_ALT, strlen(URI_PROTO_ALT))) {
		uri += strlen(URI_PROTO_ALT);
	} else {
		size += strlen(URI_PROTO);
	}
	if ('C' == toupper(uri[0]) &&
		'H' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		uri += 4;
		type = K_CHK;
	} else
	if ('S' == toupper(uri[0]) &&
		'S' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		uri += 4;
		type = K_SSK_P;
	} else 
	if ('K' == toupper(uri[0]) &&
		'S' == toupper(uri[1]) &&
		'K' == toupper(uri[2]) &&
		'@' == toupper(uri[3])) {
		uri += 4;
		type = K_KSK;
	} else {
		LOG(L_DEBUGX,("found no key-type, assuming KSK@\n"));
		size += strlen(URI_KSK);
		type = K_KSK;
	}

	normalized = xcalloc(size, sizeof(char));

	switch (type) {
	case K_CHK:
		pm_snprintf(normalized, size, "%s%s%s", URI_PROTO, URI_CHK, uri);
		break;
	case K_SSK_P:
		pm_snprintf(normalized, size, "%s%s%s", URI_PROTO, URI_SSK, uri);
		len = strlen(URI_PROTO) + strlen(URI_SSK) + KEY1SIZE - 4;
		if (0 == strncmp(normalized + len, URI_SVKEXT_ALT, 4)) {
			LOG(L_MINOR,("taking back Freenet fake '%s' to '%s'",
				URI_SVKEXT_ALT, URI_SVKEXT));
			memcpy(normalized + len, URI_SVKEXT, 4);
		}
		break;
	case K_KSK:
	default:
		pm_snprintf(normalized, size, "%s%s%s", URI_PROTO, URI_KSK, uri);
		break;
	}

	return normalized;
}

char *create_uri_from_chk(const chkey_t *key)
{
	size_t size;
	char *uri;
	size = strlen(URI_PROTO) + strlen(URI_CHK) + KEY1SIZE + 1 + KEY2SIZE + 1;
	uri = xcalloc(size, sizeof(char));
	pm_snprintf(uri, size, "%s%s%s", URI_PROTO, URI_CHK, key_str(key));
	return uri;
}

char *create_uri_from_ssk(const chkey_t *key)
{
	size_t size;
	char *uri;
	size = strlen(URI_PROTO) + strlen(URI_SSK) + KEY1SIZE + 1;
	uri = xcalloc(size, sizeof(char));
	pm_snprintf(uri, size, "%s%s%s", URI_PROTO, URI_SSK, key_str(key));
	return uri;
}

int create_chk_from_ksk(chkey_t *key, const char *ksk)
{
	char *nksk;
	size_t size;
	int rc;
	FUN("create_chk_from_ksk");

	nksk = normalize_uri(ksk);
	size = strlen(nksk);

	rc = key_chk(key, nksk, size);

	xfree(nksk);

	return rc;
}
